<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!-- 
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License. 
-->
<html>
<head>
    <link type="text/css" rel="stylesheet" href="https://struts.apache.org/css/default.css">
    <style type="text/css">
        .dp-highlighter {
            width:95% !important;
        }
    </style>
    <style type="text/css">
        .footer {
            background-image:      url('https://cwiki.apache.org/confluence/images/border/border_bottom.gif');
            background-repeat:     repeat-x;
            background-position:   left top;
            padding-top:           4px;
            color:                 #666;
        }
    </style>
    <link href='https://struts.apache.org/highlighter/style/shCoreStruts.css' rel='stylesheet' type='text/css' />
    <link href='https://struts.apache.org/highlighter/style/shThemeStruts.css' rel='stylesheet' type='text/css' />
    <script src='https://struts.apache.org/highlighter/js/shCore.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushPlain.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushXml.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushJava.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushJScript.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushGroovy.js' type='text/javascript'></script>
    <script src='https://struts.apache.org/highlighter/js/shBrushBash.js' type='text/javascript'></script>
    <script type="text/javascript">
        SyntaxHighlighter.defaults['toolbar'] = false;
        SyntaxHighlighter.all();
    </script>
    <script type="text/javascript" language="javascript">
        var hide = null;
        var show = null;
        var children = null;

        function init() {
            /* Search form initialization */
            var form = document.forms['search'];
            if (form != null) {
                form.elements['domains'].value = location.hostname;
                form.elements['sitesearch'].value = location.hostname;
            }

            /* Children initialization */
            hide = document.getElementById('hide');
            show = document.getElementById('show');
            children = document.all != null ?
                    document.all['children'] :
                    document.getElementById('children');
            if (children != null) {
                children.style.display = 'none';
                show.style.display = 'inline';
                hide.style.display = 'none';
            }
        }

        function showChildren() {
            children.style.display = 'block';
            show.style.display = 'none';
            hide.style.display = 'inline';
        }

        function hideChildren() {
            children.style.display = 'none';
            show.style.display = 'inline';
            hide.style.display = 'none';
        }
    </script>
    <title>GroovyResult</title>
</head>
<body onload="init()">
<table border="0" cellpadding="2" cellspacing="0" width="100%">
    <tr class="topBar">
        <td align="left" valign="middle" class="topBarDiv" align="left" nowrap>
            &nbsp;<a href="home.html">Home</a>&nbsp;&gt;&nbsp;<a href="faqs.html">FAQs</a>&nbsp;&gt;&nbsp;<a href="cookbook.html">Cookbook</a>&nbsp;&gt;&nbsp;<a href="groovyresult.html">GroovyResult</a>
        </td>
        <td align="right" valign="middle" nowrap>
            <form name="search" action="https://www.google.com/search" method="get">
                <input type="hidden" name="ie" value="UTF-8" />
                <input type="hidden" name="oe" value="UTF-8" />
                <input type="hidden" name="domains" value="" />
                <input type="hidden" name="sitesearch" value="" />
                <input type="text" name="q" maxlength="255" value="" />
                <input type="submit" name="btnG" value="Google Search" />
            </form>
        </td>
    </tr>
</table>

<div id="PageContent">
    <div class="pageheader" style="padding: 6px 0px 0px 0px;">
        <!-- We'll enable this once we figure out how to access (and save) the logo resource -->
        <!--img src="/wiki/images/confluence_logo.gif" style="float: left; margin: 4px 4px 4px 10px;" border="0"-->
        <div style="margin: 0px 10px 0px 10px" class="smalltext">Apache Struts 2 Documentation</div>
        <div style="margin: 0px 10px 8px 10px"  class="pagetitle">GroovyResult</div>

        <div class="greynavbar" align="right" style="padding: 2px 10px; margin: 0px;">
            <a href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=13865">
                <img src="https://cwiki.apache.org/confluence/images/icons/notep_16.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Edit Page"></a>
            <a href="https://cwiki.apache.org/confluence/pages/editpage.action?pageId=13865">Edit Page</a>
            &nbsp;
            <a href="https://cwiki.apache.org/confluence/pages/listpages.action?key=WW">
                <img src="https://cwiki.apache.org/confluence/images/icons/browse_space.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Browse Space"></a>
            <a href="https://cwiki.apache.org/confluence/pages/listpages.action?key=WW">Browse Space</a>
            &nbsp;
            <a href="https://cwiki.apache.org/confluence/pages/createpage.action?spaceKey=WW&fromPageId=13865">
                <img src="https://cwiki.apache.org/confluence/images/icons/add_page_16.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Add Page"></a>
            <a href="https://cwiki.apache.org/confluence/pages/createpage.action?spaceKey=WW&fromPageId=13865">Add Page</a>
            &nbsp;
            <a href="https://cwiki.apache.org/confluence/pages/createblogpost.action?spaceKey=WW&fromPageId=13865">
                <img src="https://cwiki.apache.org/confluence/images/icons/add_blogentry_16.gif"
                     height="16" width="16" border="0" align="absmiddle" title="Add News"></a>
            <a href="https://cwiki.apache.org/confluence/pages/createblogpost.action?spaceKey=WW&fromPageId=13865">Add News</a>
        </div>
    </div>

    <div class="pagecontent">
        <div class="wiki-content">
            <div id="ConfluenceContent"><div class="confluence-information-macro confluence-information-macro-information"><p class="title">Groovy Plugin Available</p><span class="aui-icon aui-icon-small aui-iconfont-info confluence-information-macro-icon"></span><div class="confluence-information-macro-body">
<p>There is now a Struts 2 Groovy plugin that largely superceded this page: <a shape="rect" class="external-link" href="http://cwiki.apache.org/S2PLUGINS/groovy-plugin.html">http://cwiki.apache.org/S2PLUGINS/groovy-plugin.html</a></p></div></div>
<h2 id="GroovyResult-GroovyResult-Groovyscriptsasaview">GroovyResult - Groovy scripts as a view</h2>

<p>This is an attempt to create a Result type that uses Groovy (<a shape="rect" class="external-link" href="http://groovy.codehaus.org" rel="nofollow">http://groovy.codehaus.org</a>) files as a view. It exposes the current ActionContext to a groovy script. This doesn't really have much practical use, but it's fun nonetheless and shows how easy it is to create a Result. There is another Result (JFreeChartResult) in the <a shape="rect" href="https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=117058">Cookbook</a></p>

<h2 id="GroovyResult-Installation">Installation</h2>

<p>Not much - just make sure you have Groovy in your classpath, and the antlr, asm-* and groovy jars available to your webapp.</p>

<h2 id="GroovyResult-Configuration">Configuration</h2>

<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>acdtion.xml</b></div><div class="codeContent panelContent pdl">
<pre class="brush: xml; gutter: false; theme: Default" style="font-size:12px;">
&lt;result-types&gt;
   &lt;result-type name="groovy" class="myapp.action2.extensions.GroovyResult"/&gt;
&lt;/result-types&gt;
</pre>
</div></div>
<p>xwork.xml - action definitions</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
&lt;action name="MyAction" class="myapp.action2.actions.MyAction"&gt;
  &lt;result name="success" type="groovy"&gt;
    &lt;param name="file"&gt;test.groovy&lt;/param&gt;
  &lt;/result&gt;
&lt;/action&gt;
</pre>
</div></div>
<p>The result type takes one parameter (for now), namely 'file', which contains the name of the groovy script in our script directory.</p>

<h2 id="GroovyResult-Showmethecode!">Show me the code !</h2>

<p>Here's the code of the actual GroovyResult. This is a verbose version, with a lot of error checking.</p>
<ul class="alternate"><li>source code
<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>GroovyResult.java</b></div><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
public class GroovyResult implements Result {

	public final static String GROOVY_DIR_NAME = "groovy";

	private final static Logger logger = Logger.getLogger(GroovyResult.class);
	//our groovy source file name
	private String file;
	//a groovy shell
	private GroovyShell shell;
	//our parsed script
	private Script script;
	//the outputstream that will replace the 'out' in our groovy stream
	private OutputStream out;
	//directory containing groovy scripts
	private String scriptDirectory;
	/*
	 * (non-Javadoc)
	 *
	 * @see com.opensymphony.xwork.Result#execute(com.opensymphony.xwork.ActionInvocation)
	 */
	public void execute(ActionInvocation inv) {

		//check the scriptDirectory - if it doesn't exists, use the default one
		//WEBAPP + Groovy files directory
		if (scriptDirectory == null) {
			//not pretty, but this allows us to get the app root directory
			String base = ServletActionContext.getServletContext().getRealPath(
					"/");
			//if for some reason (.war, apache connector, ..) we can't get the
			// base path
			if (base == null) {
				logger
						.warn("Could not translate the virtual path \"/\" to set the default groovy script directory");
				return;
			}
			scriptDirectory = base + GROOVY_DIR_NAME;
			//issue a warning that this directory should NOT be world readable
			// !!
			logger
					.warn("Please make sure your script directory is NOT world readable !");
		}

		// first of all, make sure our groovy file exists, is readable, and is
		// an actual file

		File groovyFile = new File(scriptDirectory, file);
		if (!groovyFile.exists()) {
			//log an error and return
			logger.warn("Could not find destination groovy file: "
					+ groovyFile.getAbsolutePath());
			return;
		}
		if (!groovyFile.isFile()) {
			//log an error and return
			logger.warn("Destination is not a file: "
					+ groovyFile.getAbsolutePath());
			return;
		}
		if (!groovyFile.canRead()) {
			//log an error and return
			logger.warn("Can not read file: " + groovyFile.getAbsolutePath());
			return;
		}

		if (logger.isDebugEnabled())
			logger.debug("File " + groovyFile.getPath()
					+ " found, going to parse it ..");

		/*
		 * Here we create a Binding object which we populate with the webwork
		 * stack
		 */
		Binding binding = new Binding();

		binding.setVariable("context", ActionContext.getContext());

		/*
		 * We replace the standard OutputStream with our own, in this case the
		 * OutputStream from our httpResponse
		 */
		try {
			//the out will be stored in an OutputStream
			out = ServletActionContext.getResponse().getOutputStream();
		} catch (IOException e1) {
			logger.error("Could not open outputstream", e1);
		}
		if (out != null){
			binding.setVariable("out", out);
		}
		else {
			logger
					.warn("OutputStream not available, using default System.out instead");
			binding.setVariable("out", System.out);
		}

		//create a new shell to parse and run our groovy file
		shell = new GroovyShell(binding);
		try {
			//try to parse the script - the returned script could be cached for
			//performance improvent
			script = shell.parse(groovyFile);
		} catch (CompilationFailedException e) {
			logger.error("Could not parse groovy script", e);
			return;
		} catch (IOException e) {
			logger.error("Error reading groovy script", e);
			return;
		}
		//the binding is set, now run the script
		Object result = script.run();

		if (logger.isDebugEnabled()) {
			logger.debug("Script " + groovyFile.getName()
					+ " executed, and returned: " + result);
		}
		try {
			out.flush();
		} catch (IOException e2) {
			logger.error("Could not flush the outputstream", e2);
		}
	}

	/**
	 * @return Returns the script.
	 */
	public Script getScript() {
		return script;
	}
	/**
	 * @param file
	 *            The file to set.
	 */
	public void setFile(String file) {
		this.file = file;
	}
	/**
	 * @param out
	 *            The out to set.
	 */
	public void setOut(OutputStream out) {
		this.out = out;
	}
</pre>
</div></div></li></ul>


<h2 id="GroovyResult-Explanation">Explanation</h2>

<p>The first part of the result is little more than:</p>
<ul class="alternate"><li>determining the script directory - defaults to <code>MYWEBAPP/groovy/</code></li><li>checking the file - make sure it exists, is readable, ..
<div class="confluence-information-macro confluence-information-macro-note"><span class="aui-icon aui-icon-small aui-iconfont-warning confluence-information-macro-icon"></span><div class="confluence-information-macro-body"><p>Make sure the groovy scripts directory is not world readable !</p></div></div></li></ul>


<div class="code panel pdl" style="border-width: 1px;"><div class="codeHeader panelHeader pdl" style="border-bottom-width: 1px;"><b>The groovy part</b></div><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
Binding binding = new Binding();
binding.setVariable("context", ActionContext.getContext());
</pre>
</div></div>
<p>A Binding object allows us to 'bind' objects to a groovy script, so they can be used as variables. In this case, I took the ActionContext and exposed it as 'context'.</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
out = ServletActionContext.getResponse().getOutputStream();
...
binding.setVariable("out", out);
</pre>
</div></div>
<p>We also bind an OutputStream to the groovy script (as 'out') - it simply serves as a replacement for the standard System.out, so any printing goes directly to the http response outputstream.</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
shell = new GroovyShell(binding);
</pre>
</div></div>
<p>Next step; we create a GroovyShell, and pass our populated Binding to the constructor. Any script ran by this shell will have access to the passed variables (ActionContext and OutputStream).</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
script = shell.parse(groovyFile);
</pre>
</div></div>
<p>Before you can run a groovyFile, you need to parse it. Any syntax errors will be reported here - I also suggest adding a better error reporting in this case if you actually want to use this Result.<br clear="none">
Upon successful parsing, a Script is returned (which could be cached if you want to increase performance) which will be run by our Shell.</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
Object result = script.run();
</pre>
</div></div>
<p>As a test, you might want to create a little 'groovy' script to test our Result.<br clear="none">
test.groovy - a simple groovy script</p>
<div class="code panel pdl" style="border-width: 1px;"><div class="codeContent panelContent pdl">
<pre class="brush: java; gutter: false; theme: Default" style="font-size:12px;">
for (item in context.contextMap){
	println "item: ${item}"
}
</pre>
</div></div>
<p>Place the test.groovy file in your groovy scripts directory. You should now see the result when you invoke MyAction.action in your browser.</p>

<p>Possible improvements are binding all objects on the stack so they become available to the groovy script, refactoring to an InputStream instead of a File, etc .. Comments welcome !</p></div>
        </div>

        
    </div>
</div>
<div class="footer">
    Generated by CXF SiteExporter
</div>
</body>
</html>
